
# SEMD validator - сервис проверки СЭМД для 1С:Медицина и сервис выдачии копий справочников ФНСИ

Сервис предназначен для выполнения проверок структурированных электронных медицинских документов (СЭМД) на соответствие xsd схеме, схематрону и справочникам ФНСИ. Схемы и схематроны публикуются на портале ЕГИСЗ Минздрава РФ. Сервис реализован как сервлет для веб сервера Jetty версии 9.4+. 

Сервис получает получает справочники ФНСИ и сохраняет в собственной in-memory базе данных. Справочники загружаются из json файлов и сохраняются в собственном бинарном формате. Встроенная база данных - база данных «ключ-значение». Ключ и значение справочника определяются по паспорту справочника и в настройках могут быть изменены. К значению справочника могут быть дописаны другие реквизиты справочника как дополнительные. Тогда сервис будет возвращать эти реквизиты вместе с основными данными (ключ + значение). Дополнительные значения в ОЗУ загружаются только в момент их использования. На значение справочника всегда строится индекс, по которому выполняется запросы LIKE и LIKE2. Индекс целиком загружается в ОЗУ в момент первого использования справочника справочника.

## Описание веб-ссылок

### /get_sch_list.html - просмотр загруженных схем и схематронов, а также списка OID справочников ФНСИ, с командами проверки
Для доступа к ссылкам требуются права администратора или доступ с localhost. В случае установки ограничений на список проверяемых типов РЭМД в параметре LIST_TYPES_FOR_VARIFICATION проверяемые типы в списке будет выделяться оформлением.

### /send_semd.html - форма отправки СЭМД для выполнения валидации
Схема и схематрон должны быть предварительно загружены на сервер.

### /verify - валидация СЭМД
 В случае проверки только на xsd-схемы и\или на соответствие справочникам ФНСИ при успешной проверке будет возвращено значение "valid". Иначе будет возращен текст сообщения об ошибке. Проверка на соответствие ФНСИ выполняется в соответствии с файлом настроек [FNSIlist.json](https://gitverse.ru/1CMedicine/semd_validator/content/master/data/FNSIlist.json).

Типы проверок:
- 0 - проверка только на xsd.
- 1 - проверка только на sch.
- 2 - проверка на xsd и sch.
- 3 - проверка на xsd, sch и ФНСИ. Точный порядок проверок - xsd, ФНСИ, sch.
  
После выполнения проверки схематроном возвращается результат проверки. Результат записывается в элементе /result/sch. В атрибуте @file элемента sch записывается имя xsl преобразования, по которому выполнялась проверка.
Пример успешной проверки:
```
<result>
<sch file="118.xsl">
<svrl:schematron-output xmlns:svrl="http://purl.oclc.org/dsdl/svrl"
                        xmlns:xs="http://www.w3.org/2001/XMLSchema"
                        xmlns:xsd="http://www.w3.org/2001/XMLSchema"
                        xmlns:saxon="http://saxon.sf.net/"
                        xmlns:schold="http://www.ascc.net/xml/schematron"
                        xmlns:iso="http://purl.oclc.org/dsdl/schematron"
                        xmlns:xhtml="http://www.w3.org/1999/xhtml"
                        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                        xmlns:identity="urn:hl7-ru:identity"
                        xmlns:address="urn:hl7-ru:address"
                        xmlns:medService="urn:hl7-ru:medService"
                        xmlns:fias="urn:hl7-ru:fias"
                        title=""
                        schemaVersion=""><!--   
		   
		   
		 -->
   <svrl:ns-prefix-in-attribute-values uri="http://www.w3.org/2001/XMLSchema-instance" prefix="xsi"/>
   <svrl:ns-prefix-in-attribute-values uri="urn:hl7-ru:identity" prefix="identity"/>
   <svrl:ns-prefix-in-attribute-values uri="urn:hl7-ru:address" prefix="address"/>
   <svrl:ns-prefix-in-attribute-values uri="urn:hl7-ru:medService" prefix="medService"/>
   <svrl:ns-prefix-in-attribute-values uri="urn:hl7-ru:fias" prefix="fias"/>
</svrl:schematron-output>
</sch>
</result>
```

Примеры неуспешных проверок:
```
:ERROR: Line: 292 Column: 186 Message: cvc-elt.4.3: Type 'CD' is not validly derived from the type definition, '#AnonType_IdentityDocTypePOCD_MT000040.DocInfo', of element 'identity:IdentityDocType'.
:ERROR: Line: 294 Column: 242 Message: cvc-elt.4.3: Type 'CD' is not validly derived from the type definition, '#AnonType_InsurancePolicyTypePOCD_MT000040.DocInfo', of element 'identity:InsurancePolicyType'.
:ERROR: INVALID_ELEMENT_VALUE_NAME Справочник OID [1.2.643.5.1.13.13.99.2.166], версия [2.7]. Наименование элемента [Наименование рекомендованного протеза, протезно-ортопедического изделия] не соответствует наименованию элемента в НСИ [Сведения о протезе, протезно-ортопедическом изделии]
```


```
<result>
<sch file="118.xsl">
<svrl:schematron-output xmlns:svrl="http://purl.oclc.org/dsdl/svrl"
                        xmlns:xs="http://www.w3.org/2001/XMLSchema"
                        xmlns:xsd="http://www.w3.org/2001/XMLSchema"
                        xmlns:saxon="http://saxon.sf.net/"
                        xmlns:schold="http://www.ascc.net/xml/schematron"
                        xmlns:iso="http://purl.oclc.org/dsdl/schematron"
                        xmlns:xhtml="http://www.w3.org/1999/xhtml"
                        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
                        title=""
                        schemaVersion="iso">
   <svrl:ns-prefix-in-attribute-values uri="http://www.w3.org/2001/XMLSchema-instance" prefix="xsi"/>
   <svrl:failed-assert test="count(realmCode)=1">
      <svrl:text>У1-8. Элемент ClinicalDocument должен иметь один элемент realmCode.</svrl:text>
   </svrl:failed-assert>
</svrl:schematron-output>
</sch>
</result>
```

### /data - получение содержимого справочников ФНСИ
Выдает содержимое справочника в JSON формате ФНСИ. Возможно наложение отбора на поле, которое в файле FNSIlists.json определено как KEY и на поле, которое определено как VALUE.
Если в настройках справочника перечислены поля в поле REKV, то будут выданы и значения эти полей.
Формат запросов сделан по аналогии с аналогичными запросами к ФНСИ.

Параметры:
- userKey - идентификатор запроса. Формат не контролируется.
Идентификатор должен быть уникальным для пагинированных запросов в рамках получения страниц. 
То есть запрос на получение N-ой страницы должен иметь тот же идентификатор, что и на N-1.
Для пагинированных запросов в параметре page значение номера страницы не важно, тут
используется логика работы курсоров в СУБД. Вызов интерпретируется как next у курсора.
Обязательный.
- identifier - oid справочника ФНСИ. Обязательный.
- remdtype - тип РЭМД по справочнику 1.2.643.5.1.13.13.11.1520. Если указан, то справочник будет использован той версии, которая указана в 1.2.643.5.1.13.13.99.2.805 для требуемого типа РЭМД. Необязательный.
- version - номер версии справочника ФНСИ. Если номер версии не указан, то используется последняя версия справочника. Если указан remdtype, то параметр не используется. Необязательный.
- size - размер страницы в записях. Если параметр size не указан, то берется значение по умолчанию - 20. Необязательный.
- filters - критерии отбора записей справочника. Если в качестве имени поля указано поле KEY, то осуществляется поиск значения этого поля. Применение модификатора LIKE не предусмотрено. Необязательный.

Если в качестве имени поля указано поле VALUE, то необходимо использовать один из 2-х модификаторов:
- LIKE - запись соответствует критерию поиска, если в Value входит искомая подстрока (без учета регистра)
- LIKE2 - запись соответствует критерию поиска, если в Value входят искомые подслова (без учета регистра). Поиск морфологический - без учета окончаний существительных и прилагательных

Примеры:
```
data?userKey=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx&identifier=1.2.643.5.1.13.13.11.1522&version=1.2&filters=code|1
data?userKey=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx&identifier=1.2.643.5.1.13.13.11.1522&remdtype=114&filters=code|1
data?userKey=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx&identifier=1.2.643.5.1.13.13.11.1522&size=20&page=2
data?userKey=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx&identifier=1.2.643.5.1.13.13.11.1522&filters=Name|Протокол|LIKE&size=20&page=2
data?userKey=xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx&identifier=1.2.643.5.1.13.13.11.1522&filters=name|наименование|LIKE2&size=20&page=2
```

Примеры использования LIKE:
```
Шаблон 'медицинская сес'

Данные:
'медицинская сестра' - соответствует
'сестра медицинская' - не соответствует

Шаблон 'едицинск'
 
Данные:
'медицинская сестра' - соответствует
```

Примеры использования LIKE2:
```
Шаблон 'медицинская сес'
 
Данные:
'медицинская сестра' - соответствует
'сестра медицинская' - соответствует

Шаблон 'едицинск'

Данные:
'медицинская сестра' - не соответствует
```

В ответе сервиса возвращаются сведения о справочнике, по которому был сформирован ответ - codeSystem, codeSystemName, codeSystemVersion.

Данные ответа:
- result - статус ответа. Достимые значения - OK и ERROR.
- total - число записей в ответе. Если ответ пагинированный, то число записей в странице.
- hasNext - есть ли следующая страница (для пагинированных ответов).
- left - данные ответа. В качестве идентификаторов колонок используются имена колонок справочника ФНСИ. См. раздел FNSIlist.json.

Пример ответа:
```
{"result":"OK",
"total":4,
"codeSystem":"1.2.643.5.1.13.13.11.1035","codeSystemName":"Виды полиса обязательного медицинского страхования","codeSystemVersion":"1.3",
"hasNext":false,
"list":[
   {"column":"ID","value":"4"},
   {"column":"NAME","value":"Временное свидетельство"}
   ]
}
```


### /send_sch.html - форма отправки файлов настроек в сервис

#### Список тегов, подлежащих проверке на соответствие справочникам ФНСИ
Список тегов передается в сервис текстовым файлом в кодировке UTF-8. Теги записывается построчно. Тег должен содержать следующие атрибуты, значение которых подлежит контролю - code, codeSystemVersion, codeSystemName, displayName. Если есть namespace, то тег указывается с namespace.
Пример содержимого:
```
code
administrativeGenderCode
value
functionCode
translation
```



### /updatedb - генерации xsl для схематронов 
Sch файлы уже должны находится в папке DATA_PATH. Требования к файлам такие же как как к отправляемым в zip архиве через форму send_sch.html
Для доступа к ссылкам требуются права администратора или доступ с localhost. В URL передается список типов РЭМД, которым необходимо сгенерировать XSL для валидации. Список передается через разделитель - "_".

Примеры:
 ```
http://localhost:8891/a/semd/updatedb/223
http://localhost:8891/a/semd/updatedb/223_224
 ```

Возвращаемые значение - текст 'OK' и далее через пробел список типов РЭМД, которые успешно обработались.
В случае возникновения ошибки команда вернет текст ошибки.

### /login.html - форма авторизации

## Папка data 
xsd схемы и схематроны для СЭМД, поддерживаемых 1С:Медицина. Содержимое папки можно использовать для наполнения папки, указанной в параметре сервлета DATA_PATH.

Для предоставления прав записи в эту папку в некоторых дистрибутивах необходимо в настройка sandbox сервиса Jetty дать права записи. Например, в Ubuntu 20.04 необходимо в файле /etc/systemd/system/multi-user.target.wants/jetty9.service добавить строку (новую):
 ```
 ReadWritePaths=/var/www/upload/tron2/
 ```
Для применения настроек надо выполнить:
 ```
systemctl daemon-reload
service jetty9 restart
 ```

В папке data должна быть создана папка avl с такими же правами доступа как к папке data. В папке avl хранятся скачанные справочники ФНСИ в объеме, заданном для в 
файле [FNSIlist.json](https://gitverse.ru/1CMedicine/semd_validator/content/master/data/FNSIlist.json). Загрузка справочников ФНСИ выполняет консольной утилитой fnsi-downloader. 


## Параметры сервлета
В файле semd.war:\WEB-INF\web.xml задаются следующие параметры:
- DATA_PATH - полный путь к папке, где будут хранится загруженный файлы (xsd, sch и сгенерированные xsl файлы). Значение по умолчанию - /var/www/upload/tron2.
- ADMIN_NAME - имя пользователя, являющего администратором. Значение по умолчанию - admin.
- ADMIN_PASS - пароль пользователя, являющего администратором. Значение по умолчанию - pass.
- LIST_TYPES_FOR_VARIFICATION - список типов РЭМД (OID), по которым выполняется проверка. Типы РЭМД указывают через запятую, без пробелов. 
  Если список пустой, то ограничений нет и проверка выполняется по всем типам, по которым загружены xsd и sch.
- DB - путь к файлу базы данных SQLite3


## Установка сервлета
Для работы сервлета semd.war нужен Jetty версии 9.4 или выше. Необходимо опубликовать сервлет semd.war в Jetty 9. Необходимо руководствоваться инструкций по публикации Jetty. 

Для Jetty9 из состава Ubuntu 20.04 файл semd.war необходимо поместить в папку /var/lib/jetty9/webapps. Отвечать сервлет будет по адресу "http:/localhost:9000/semd/login.html".


### Инициализация SQLite3 базы 

Создание базы
```
sqlite3 fnsi.db
```

Инициализаця базы, ниже приведены команды для выполнения в консоли sqlite3
```
create table if not exists fnsi_emd (
    ID INTEGER PRIMARY KEY NOT NULL,
    TYPE INTEGER NOT NULL,
    NAME TEXT NOT NULL,
    START_DATE TEXT NOT NULL,
    END_DATE TEXT NULL,
    IMPLEMENTATION_GUIDE TEXT NULL,
    GIT_LINK TEXT NULL              -- 1.2.643.5.1.13.13.99.2.638
);
create table if not exists fnsi_ref (
    codeSystem TEXT NOT NULL,       -- OID справочника ФНСИ
    version TEXT NULL,              -- версия справочника фнси
    codeSystemName TEXT NULL,       -- наименование справочника
    rowsCount INT NOT NULL,
    codeSystemOther TEXT,
    keyField TEXT NOT NULL,         -- наименование поля с ключом справочника
    valueField TEXT,                -- наименование поля со значением справочника
    fieldNames TEXT,                -- данные паспорта - список наименований полей. 
    actual TEXT,                    -- признак действия версии справочника, если * - то версия загружена была по запросу актуальной версии
    PRIMARY KEY(codeSystem, version)
) WITHOUT ROWID;
create table if not exists ref_setting (
    codeSystem TEXT PRIMARY KEY,                         -- OID справочника ФНСИ
    load INT NOT NULL CHECK(load IN(0,1)),               -- загружать или нет справочник
    validateRefName INT CHECK(validateRefName IN(0,1)),  -- выполнять ли по нему проверку codeSystemName в СЭМД
    keyField TEXT,                                       -- наименование поля с ключом справочника
    valueField TEXT,                                     -- наименование поля со значением справочника
    validateValue INT CHECK(load IN(0,1)),               -- выполнять ли по нему проверку displayName в СЭМД
    REKV TEXT,                                           -- JSON массив со списком полей, которые надо загрузить
    comment TEXT
) WITHOUT ROWID;
create table if not exists fnsi_semd_refs (
    ID TEXT PRIMARY KEY NOT NULL, 
    OID TEXT NOT NULL,              -- 1.2.643.5.1.13.13.99.2.638
    REF TEXT NOT NULL,              -- OID справочника ФНСИ (codeSystem)
    VERSION TEXT NULL,              -- версия справочника ФНСИ
    REF_NAME TEXT NOT NULL          -- наименование справочника (codeSystemName)
) WITHOUT ROWID;
create table if not exists fnsi_ref_ver (
    OID TEXT PRIMARY KEY NOT NULL,
    VERSION TEXT NOT NULL
) WITHOUT ROWID;
insert into fnsi_ref_ver values ('1.2.643.5.1.13.13.11.1520','');
insert into fnsi_ref_ver values ('1.2.643.5.1.13.13.99.2.805','');
```

## FNSIlist.json - настройки использования справочников ФНСИ
Массив объектов в формате JSON, где для справочников указываются следующие настройки:
- CODESYSTEM - oid справочника ФНСИ
- LOAD - если указано значение 0 (false), то справочник не загружается в сервис валидации СЭМД. 
- VALIDATE_REFNAME - выполнять проверку атрибута codeSystemName в СЭМД.
- KEY - имя поля справочника ФНСИ, которое будет использоваться как ключ справочника. При проверке СЭМД это поле соотвествует атрибуту code. Если значение не указано, то берется поле, помеченное как PRIMARY в паспорте справочника.
- VALUE - имя поля справочника ФНСИ, которое будет использоваться как значение справочника.  При проверке СЭМД это поле соотвествует атрибуту displayName. Если значение не указано, то берется поле, помеченное как VALUE в паспорте справочника.
- VALIDATE_VALUE - если указано значение 0 (false), то при проверке СЭМД не выполняется проверка displayName. 
- REKV - список полей справочника, которые будут сохранены при загрузке справочника и будут выдаваться запросами data к сервису валидации
- COMMENT - комментарий к справочнику

# fnsi-downloader

Параметры командной строки
- DB - путь к файлу базы данных SQLite3. Должен совпадать с аналогичным параметром сервлета.
- URL - адрес публикации сервлета. ВАЖНО, чтобы доступ к сервлету был через localhost, т.к. сервлет без авторизмации принимает команды только с localhost.
Параметр необязательный, если он не указан, то обращений по актуализации ФНСИ в сервлете не производится
- USERKEY - ключ доступа к сайту nsi.rosmindrav.ru
- DATA_PATH - папка, где располагается файл FNSIlist.json (настройки использования справочников ФНСИ), в этой же папке должна быть создана папка avl, куда будут сохраняться бинарные версии справочников. Должен совпадать с аналогичным параметром сервлета.

Пример запуска
```
java -jar fnsi-downloader-1.0-jar-with-dependencies.jar DB=/home/user/fnsi.db DATA_PATH=/home/user/ URL=http://infomed.med.1c.ru/a/semd USERKEY=xxx-xxx-xxx-xxx
```


Для запуска fnsi-downloader может потребоваться установка сертификата сайта ФНСИ в хранилище сертификатов Java. Для этого необходимо в браузере сохранить сертификат сайта nsi.rosminzdrav.ru в бинарном виде (DER), после чего необходимо экспортировать его в lib\security\cacerts:
```
keytool -import -alias rosminzdrav -keystore  D:\openjdk-17.0.2\lib\security\cacerts -file _.rosminzdrav.ru.der
```



## Сборка
Для сборки проекта необходимо выполнить команду в корневой папке проекта
 ```
mvn package
 ```
в папке, где расположен файл pom.xml, который определяет проект, состоящий из 3-х модулей - servlet, console, avltree.

Подробнее о Maven можно найти на странице https://maven.apache.org/guides/getting-started/maven-in-five-minutes.html.